
#include "platform_def.h"
#include "reg_soc.h"
#include "phy_pll_init.h"
#include "ddr_sys.h"

// plat/cv180x/include/ddr/ddr_config/ddr2_1333_x16/ddr_init.h
#include "ddr_init.h"

#include "mmio.h"
#include "bitwise_ops.h"
#include "cvx16_dram_cap_check.h"
#include "cvx16_pinmux.h"
#include "regconfig.h"
// #include "console.h"
#include "ddr_pkg_info.h"

#define DO_BIST

#define REMAPPING_BASE 0
#define AXIMON_M1_WRITE	(REMAPPING_BASE + 0x0)
#define AXIMON_M1_READ	(REMAPPING_BASE + 0x80)
#define AXIMON_M2_WRITE	(REMAPPING_BASE + 0x100)
#define AXIMON_M2_READ	(REMAPPING_BASE + 0x180)
#define AXIMON_M3_WRITE	(REMAPPING_BASE + 0x200)
#define AXIMON_M3_READ	(REMAPPING_BASE + 0x280)
#define AXIMON_M4_WRITE	(REMAPPING_BASE + 0x300)
#define AXIMON_M4_READ	(REMAPPING_BASE + 0x380)
#define AXIMON_M5_WRITE	(REMAPPING_BASE + 0x400)
#define AXIMON_M5_READ	(REMAPPING_BASE + 0x480)
#define AXIMON_M6_WRITE	(REMAPPING_BASE + 0x500)
#define AXIMON_M6_READ	(REMAPPING_BASE + 0x580)

#define AXIMON_OFFSET_CYCLE 0x24
#define AXIMON_OFFSET_BYTECNTS 0x2C
#define AXIMON_OFFSET_LATENCYCNTS 0x34
#define AXIMON_OFFSET_HITCNTS 0x28
#define AXIMON_OFFSET_LAT_BIN_SIZE_SEL 0x50

#define AXIMON_START_REGVALUE 0x30001
#define AXIMON_STOP_REGVALUE 0x30002

static void axi_mon_latency_setting(uint32_t lat_bin_size_sel)
{
	uint32_t rdata;

	mmio_wr32((AXI_MON_BASE + AXIMON_M1_WRITE + AXIMON_OFFSET_LAT_BIN_SIZE_SEL),
					lat_bin_size_sel);//for ddr3 1866: bin_size_sel=0d'5
	mmio_wr32((AXI_MON_BASE + AXIMON_M1_READ + AXIMON_OFFSET_LAT_BIN_SIZE_SEL),
					lat_bin_size_sel);

	mmio_wr32((AXI_MON_BASE + AXIMON_M1_WRITE + 0x00), 0x01000100);//input clk sel
	rdata = mmio_rd32((AXI_MON_BASE + AXIMON_M1_WRITE + 0x04));//hit sel setting
	rdata = rdata & 0xfffffc00;
	rdata = rdata | 0x00000000;
	mmio_wr32((AXI_MON_BASE + AXIMON_M1_WRITE + 0x04), rdata);

	mmio_wr32((AXI_MON_BASE + AXIMON_M1_READ + 0x00), 0x01000100);
	rdata = mmio_rd32((AXI_MON_BASE + AXIMON_M1_READ + 0x04));
	rdata = rdata & 0xfffffc00;
	rdata = rdata | 0x00000000;
	mmio_wr32((AXI_MON_BASE + AXIMON_M1_READ + 0x04), rdata);

	mmio_wr32((AXI_MON_BASE + AXIMON_M5_WRITE + AXIMON_OFFSET_LAT_BIN_SIZE_SEL), lat_bin_size_sel);
	mmio_wr32((AXI_MON_BASE + AXIMON_M5_READ + AXIMON_OFFSET_LAT_BIN_SIZE_SEL), lat_bin_size_sel);

	mmio_wr32((AXI_MON_BASE + AXIMON_M5_WRITE + 0x00), 0x01000100);
	rdata = mmio_rd32((AXI_MON_BASE + AXIMON_M5_WRITE + 0x04));
	rdata = rdata & 0xfffffc00;
	rdata = rdata | 0x00000000;
	mmio_wr32((AXI_MON_BASE + AXIMON_M5_WRITE + 0x04), rdata);

	mmio_wr32((AXI_MON_BASE + AXIMON_M5_READ + 0x00), 0x01000100);
	rdata = mmio_rd32((AXI_MON_BASE + AXIMON_M5_READ + 0x04));
	rdata = rdata & 0xfffffc00;
	rdata = rdata | 0x00000000;
	mmio_wr32((AXI_MON_BASE + AXIMON_M5_READ + 0x04), rdata);

	//NOTICE("mon cg en.\n");
	rdata = mmio_rd32((DDR_TOP_BASE+0x14));
	rdata = rdata | 0x00000100;
	mmio_wr32((DDR_TOP_BASE+0x14), rdata);
}

static void axi_mon_start(uint32_t base_register)
{
	mmio_wr32((AXI_MON_BASE + base_register), AXIMON_START_REGVALUE);
}

void axi_mon_start_all(void)
{
	axi_mon_start(AXIMON_M1_WRITE);
	axi_mon_start(AXIMON_M1_READ);
	
	axi_mon_start(AXIMON_M2_WRITE);
	axi_mon_start(AXIMON_M2_READ);
	
	axi_mon_start(AXIMON_M3_WRITE);
	axi_mon_start(AXIMON_M3_READ);
	
	axi_mon_start(AXIMON_M4_WRITE);
	axi_mon_start(AXIMON_M4_READ);
	
	axi_mon_start(AXIMON_M5_WRITE);
	axi_mon_start(AXIMON_M5_READ);
	
	axi_mon_start(AXIMON_M6_WRITE);
	axi_mon_start(AXIMON_M6_READ);
}



void ddr_sys_bring_up( void )
{
	uint32_t bist_result;
	uint64_t err_data_even, err_data_odd;
	uint8_t dram_cap_in_mbyte;


	uartlog("%s pattern!\n", __func__);

	// mmio_wr32(PLLG6_BASE+top_pll_g6_reg_ddr_ssc_syn_src_en,
	//            mmio_rd32(PLLG6_BASE+top_pll_g6_reg_ddr_ssc_syn_src_en)
	//            &(~top_pll_g6_reg_ddr_ssc_syn_src_en_MASK)
	//            |0x1<<top_pll_g6_reg_ddr_ssc_syn_src_en_OFFSET);


	uartlog("PLL INIT !\n");
	pll_init();


	uartlog("DDRC_INIT !\n");
	ddrc_init();

	// cvx16_ctrlupd_short();

	// release ddrc soft reset
	uartlog("releast reset  !\n");
	mmio_wr32(DDR_TOP_BASE + 0x20, 0x0);

	// set axi QOS
	// M1 = 0xA (VIP realtime)
	// M2 = 0x8 (VIP offline)
	// M3 = 0x7 (CPU)
	// M4 = 0x0 (TPU)
	// M5 = 0x9 (Video codec)
	// M6 = 0x2 (high speed peri)
	mmio_wr32(0x030001D8, 0x007788aa);
	mmio_wr32(0x030001DC, 0x00002299);


	uartlog("phy_init !\n");
	phy_init();

	cvx16_setting_check();
	KC_MSG("cvx16_setting_check  finish\n");

	// pinmux
	cvx16_pinmux();
	KC_MSG("cvx16_pinmux finish\n");

	ddr_patch_set();

	cvx16_en_rec_vol_mode();
	KC_MSG("cvx16_en_rec_vol_mode finish\n");

	// set_dfi_init_start
	cvx16_set_dfi_init_start();
	KC_MSG("set_dfi_init_start finish\n");

	// ddr_phy_power_on_seq1
	cvx16_ddr_phy_power_on_seq1();
	KC_MSG("ddr_phy_power_on_seq1 finish\n");

	// first dfi_init_start
	KC_MSG("first dfi_init_start\n");
	cvx16_polling_dfi_init_start();
	KC_MSG("cvx16_polling_dfi_init_start finish\n");

	cvx16_INT_ISR_08();
	KC_MSG("cvx16_INT_ISR_08 finish\n");

	// ddr_phy_power_on_seq3
	cvx16_ddr_phy_power_on_seq3();
	KC_MSG("ddr_phy_power_on_seq3 finish\n");

	// wait_for_dfi_init_complete
	cvx16_wait_for_dfi_init_complete();
	KC_MSG("wait_for_dfi_init_complete finish\n");

	// polling_synp_normal_mode
	cvx16_polling_synp_normal_mode();
	KC_MSG("polling_synp_normal_mode finish\n");

#ifdef DO_BIST
	cvx16_bist_wr_prbs_init();
	cvx16_bist_start_check(&bist_result, &err_data_odd, &err_data_even);
	KC_MSG(", bist_result = %x, err_data_odd = %lx, err_data_even = %lx\n", bist_result, err_data_odd,
	       err_data_even);
	if (bist_result == 0) {
		ERROR("ERROR bist_fail\n");
		ERROR("bist_result = %lx, err_data_odd = %llx, err_data_even = %llx\n", bist_result, err_data_odd, err_data_even);
	}
#endif

	ctrl_init_low_patch();
	KC_MSG("ctrl_low_patch finish\n");


#ifdef DO_BIST
	cvx16_bist_wr_prbs_init();
	cvx16_bist_start_check(&bist_result, &err_data_odd, &err_data_even);
	KC_MSG(", bist_result = %lx, err_data_odd = %llx, err_data_even = %llx\n", bist_result, err_data_odd, err_data_even);
	if (bist_result == 0) {
		ERROR("ERROR bist_fail\n");
		ERROR("bist_result = %lx, err_data_odd = %llx, err_data_even = %llx\n", bist_result, err_data_odd, err_data_even);
	}
#endif
	
	// cvx16_rdglvl_req
	cvx16_rdglvl_req();
	KC_MSG("cvx16_rdglvl_req finish\n");
	
#ifdef DO_BIST
	cvx16_bist_wr_prbs_init();
	cvx16_bist_start_check(&bist_result, &err_data_odd, &err_data_even);
	KC_MSG(", bist_result = %lx, err_data_odd = %llx, err_data_even = %llx\n", bist_result, err_data_odd, err_data_even);
	if (bist_result == 0) {
		ERROR("ERROR bist_fail\n");
		ERROR("bist_result = %lx, err_data_odd = %llx, err_data_even = %llx\n", bist_result, err_data_odd, err_data_even);
	}
#endif

	//ERROR("AXI mon setting for latency histogram.\n");
	//axi_mon_set_lat_bin_size(0x5);

#ifdef DBG_SHMOO
	// DPHY WDQ
	// param_phyd_dfi_wdqlvl_vref_start [6:0]
	// param_phyd_dfi_wdqlvl_vref_end [14:8]
	// param_phyd_dfi_wdqlvl_vref_step [19:16]
	mmio_wr32(0x08000190, 0x00021E02);

	// param_phyd_piwdqlvl_dly_step[23:20]
	mmio_wr32(0x080000a4, 0x01220504);

	// write start   shift = 5  /  dline = 78
	mmio_wr32(0x080000a0, 0x0d400578);

	//write
	KC_MSG("wdqlvl_M1_ALL_DQ_DM\n");
	// data_mode = 'h0 : phyd pattern
	// data_mode = 'h1 : bist read/write
	// data_mode = 'h11: with Error enject,  multi- bist write/read
	// data_mode = 'h12: with Error enject,  multi- bist write/read
	// lvl_mode  = 'h0 : wdmlvl
	// lvl_mode  = 'h1 : wdqlvl
	// lvl_mode  = 'h2 : wdqlvl and wdmlvl
	// cvx16_wdqlvl_req(data_mode, lvl_mode)
	NOTICE("cvx16_wdqlvl_sw_req dq/dm\n"); console_getc();
	cvx16_wdqlvl_sw_req(1, 2);
	// cvx16_wdqlvl_status();
	KC_MSG("cvx16_wdqlvl_req dq/dm finish\n");

	NOTICE("cvx16_wdqlvl_sw_req dq\n"); console_getc();
	cvx16_wdqlvl_sw_req(1, 1);
	// cvx16_wdqlvl_status();
	KC_MSG("cvx16_wdqlvl_req dq finish\n");

	NOTICE("cvx16_wdqlvl_sw_req dm\n"); console_getc();
	cvx16_wdqlvl_sw_req(1, 0);
	// cvx16_wdqlvl_status();
	NOTICE("cvx16_wdqlvl_req dm finish\n");

#else //DBG_SHMOO

	// cvx16_wdqlvl_req
	KC_MSG("wdqlvl_M1_ALL_DQ_DM\n");
	// sso_8x1_c(5, 15, 0, 1, &sram_sp);//mode = write, input int fmin = 5, input int fmax = 15,
					    //input int sram_st = 0, output int sram_sp

	// data_mode = 'h0 : phyd pattern
	// data_mode = 'h1 : bist read/write
	// data_mode = 'h11: with Error enject,  multi- bist write/read
	// data_mode = 'h12: with Error enject,  multi- bist write/read
	// lvl_mode  = 'h0 : wdmlvl
	// lvl_mode  = 'h1 : wdqlvl
	// lvl_mode  = 'h2 : wdqlvl and wdmlvl
	// cvx16_wdqlvl_req(data_mode, lvl_mode);
	cvx16_wdqlvl_req(1, 2);
	KC_MSG("cvx16_wdqlvl_req dq/dm finish\n");

	cvx16_wdqlvl_req(1, 1);
	KC_MSG("cvx16_wdqlvl_req dq finish\n");

	cvx16_wdqlvl_req(1, 0);
	KC_MSG("cvx16_wdqlvl_req dm finish\n");

#ifdef DO_BIST
	cvx16_bist_wr_prbs_init();
	cvx16_bist_start_check(&bist_result, &err_data_odd, &err_data_even);
	KC_MSG(", bist_result = %x, err_data_odd = %lx, err_data_even = %lx\n", bist_result, err_data_odd, err_data_even );
	if ( bist_result == 0 )  {
		KC_MSG("ERROR bist_fail\n");
	}
#endif

#endif //!DBG_SHMOO


#ifdef DBG_SHMOO
	// param_phyd_pirdlvl_dly_step [3:0]
	// param_phyd_pirdlvl_vref_step [11:8]
	mmio_wr32(0x08000088, 0x0A010212);

	//read
	NOTICE("cvx16_rdlvl_req start\n"); console_getc();
	NOTICE("SW mode 1, sram write/read continuous goto\n");
	cvx16_rdlvl_sw_req(1);
	// cvx16_rdlvl_status();
	NOTICE("cvx16_rdlvl_req finish\n");

#else //DBG_SHMOO

	// cvx16_rdlvl_req
	// mode = 'h0  : MPR mode, DDR3 only.
	// mode = 'h1  : sram write/read continuous goto
	// mode = 'h2  : multi- bist write/read
	// mode = 'h10 : with Error enject,  multi- bist write/read
	// mode = 'h12 : with Error enject,  multi- bist write/read
	rddata = mmio_rd32(0x008c + PHYD_BASE_ADDR);
	rddata = modified_bits_by_value(rddata, 1, 7, 4); // param_phyd_pirdlvl_capture_cnt
	mmio_wr32(0x008c + PHYD_BASE_ADDR, rddata);

	KC_MSG("mode multi- bist write/read\n");
	// cvx16_rdlvl_req(2); // mode multi- PRBS bist write/read
	cvx16_rdlvl_req(1); // mode multi- SRAM bist write/read
	KC_MSG("cvx16_rdlvl_req finish\n");
#ifdef DO_BIST
	cvx16_bist_wr_prbs_init();
	cvx16_bist_start_check(&bist_result, &err_data_odd, &err_data_even);
	KC_MSG(", bist_result = %x, err_data_odd = %lx, err_data_even = %lx\n", bist_result, err_data_odd,
	       err_data_even);
	if (bist_result == 0) {
		KC_MSG("ERROR bist_fail\n");
	}
#endif
#endif //!DBG_SHMOO


	// ctrl_high_patch
	ctrl_init_high_patch();

	ctrl_init_detect_dram_size(&dram_cap_in_mbyte);
	KC_MSG("ctrl_init_detect_dram_size finish\n");

	ctrl_init_update_by_dram_size(dram_cap_in_mbyte);
	KC_MSG("ctrl_init_update_by_dram_size finish\n");

	KC_MSG("dram_cap_in_mbyte = %x\n", dram_cap_in_mbyte);
	cvx16_dram_cap_check(dram_cap_in_mbyte);
	KC_MSG("cvx16_dram_cap_check finish\n");

	// clk_gating_enable
	cvx16_clk_gating_enable();
	KC_MSG("cvx16_clk_gating_enable finish\n");

#ifdef DO_BIST
	cvx16_bist_wr_prbs_init();
	cvx16_bist_start_check(&bist_result, &err_data_odd, &err_data_even);
	
	KC_MSG(", bist_result = %x, err_data_odd = %lx, err_data_even = %lx\n", bist_result, err_data_odd, err_data_even );

	if (bist_result == 0) {
		KC_MSG("ERROR prbs bist_fail\n");
		NOTICE("DDR BIST FAIL\n");
		while (1) {
		}
	}

	cvx16_bist_wr_sram_init();
	cvx16_bist_start_check(&bist_result, &err_data_odd, &err_data_even);
	
	KC_MSG(", bist_result = %x, err_data_odd = %lx, err_data_even = %lx\n", bist_result, err_data_odd, err_data_even );
	
	if (bist_result == 0) {
		KC_MSG("ERROR sram bist_fail\n");
		NOTICE("DDR BIST FAIL\n");
		while (1) {
		}
	}
	
	NOTICE("DDR BIST PASS\n");
#endif


	//NOTICE("AXI mon setting for latency histogram.\n");
	axi_mon_latency_setting(0x5);

	//NOTICE("AXI mon 0 register dump before start.\n");
	//dump_axi_mon_reg(AXIMON_M1_WRITE);
	//NOTICE("AXI mon 1 register dump before start.\n");
	//dump_axi_mon_reg(AXIMON_M1_READ);

	axi_mon_start_all();
}


#define read_csr(reg) ({ unsigned long __tmp; \
	asm volatile ("csrr %0, " #reg : "=r"(__tmp)); \
	__tmp; })


#define SYS_COUNTER_FREQ_IN_US (SYS_COUNTER_FREQ_IN_SECOND / 1000000)

static uint32_t get_timer_value(void)
{
	/*
	 * Generic delay timer implementation expects the timer to be a down
	 * counter. We apply bitwise NOT operator to the tick values returned
	 * by read_cntpct_el0() to simulate the down counter. The value is
	 * clipped from 64 to 32 bits.
	 */
	return (uint32_t)(~read_csr(time));
}

void trig_simulation_timer(uint32_t usec)
{
	uint32_t total_delta = (usec * SYS_COUNTER_FREQ_IN_US);

	// Trig simulation bench to increse cntpct_el0
	mmio_write_32(REG_GP_REG2, total_delta);
}

/***********************************************************
 * Delay for the given number of microseconds. The driver must
 * be initialized before calling this function.
 ***********************************************************/
void udelay( uint32_t usec )
{
	uint32_t start, delta, total_delta;

	start = get_timer_value();

	total_delta = (usec * SYS_COUNTER_FREQ_IN_US);

	// Trig simulation bench to increse cntpct_el0
	mmio_write_32(REG_GP_REG2, total_delta);

	do {
		/*
		 * If the timer value wraps around, the subtraction will
		 * overflow and it will still give the correct result.
		 */
		delta = start - get_timer_value(); /* Decreasing counter */

	} while (delta < total_delta);
}


